﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using Kugar.Core.BaseStruct;
using Kugar.Core.ExtMethod;
using System.Threading;
using System.ServiceModel.Channels;
using Kugar.Core.Collections;

namespace Kugar.Core.Communications.Transfer
{
    public class TCPTransferBase : TransferBase, INetTransfer
    {
        //private Socket socket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
        private Socket _socket;
        private int _receiveBufferCount;
        private bool isBufferCountChanged = false;
        private BufferManager _bufferManager = null;
        private bool isClosed;
        private bool _isOwner = false;
        private DataPacketHandle dataPacketHandle = null;
        private byte[] _revBuffer = null;
        private int _lastRevBufferPos = 0;
        private int _localPort;
        private IPEndPoint _remotePoint;
        private bool _isStop = false;
        private bool _isNeedSpliteBag=false;
        //private IntIndexDictioanry<IAsyncResult> _cacheSendAsync=new IntIndexDictioanry<IAsyncResult>();
        private Dictionary<IAsyncResult, TCPCallbackState> _cacheAsyncResult = new Dictionary<IAsyncResult, TCPCallbackState>();
        private CircularQueue<ArraySegment<byte>> _cacheReceiveData = new CircularQueue<ArraySegment<byte>>(2);
        //private TCPReceiveAsyncResult _revAsyncResult = null;
        private SocketAsyncEventArgs _internalReceiveAsync = new SocketAsyncEventArgs();
        //private SocketAsyncEventArgs _internalSendAsync=new SocketAsyncEventArgs();
        private SocketAsyncEventArgsPool _socketAsyncPool = null;
        private TransferAsyncEventArgs _receiveAsync = null;


        public TCPTransferBase(int localPort,bool isNeedSpliteBag, IPEndPoint removePoint, int receiveDataMaxLength)
        {
            if (receiveDataMaxLength <= 0)
            {
                throw new ArgumentOutOfRangeException("receiveDataMaxLength");
            }

            _receiveBufferCount = receiveDataMaxLength;
            _isOwner = true;
            _isNeedSpliteBag = isNeedSpliteBag;
            //_revBuffer = new byte[_receiveBufferCount];
            //buffer=new byte[receiveBufferCount];
            //bufferManager = BufferManager.CreateBufferManager(receiveDataMaxLength * 10, _receiveBufferCount);

            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            try
            {
                _socket.Bind(new IPEndPoint(IPAddress.Loopback, localPort));

                _socket.Connect(removePoint);
            }
            catch (Exception)
            {
                throw;
            }

            Init();

            //_socket = socket;

            //dataPacketHandle = new DataPacketHandle(bufferManager);

            //_localPort = ((IPEndPoint)socket.LocalEndPoint).Port;
            //_remotePoint = removePoint;

            //if (socket != null)
            //{
            //_revBuffer = bufferManager.TakeBuffer(_receiveBufferCount);
            //socket.BeginReceive(_revBuffer, 0, _revBuffer.Length, SocketFlags.None, OnReceiveData, _revBuffer);
            //}
        }

        public TCPTransferBase(Socket socket, int receiveDataMaxLength) : this(socket,true, receiveDataMaxLength, false) { }

        public TCPTransferBase(Socket socket,bool isNeedSpliteBag, int receiveDataMaxLength, bool isOwner)
        {
            if (receiveDataMaxLength <= 0)
            {
                throw new ArgumentOutOfRangeException("receiveDataMaxLength");
            }

            if (socket == null)
            {
                throw new ArgumentNullException("socket");
            }

            _receiveBufferCount = receiveDataMaxLength ;
            //_revBuffer = new byte[_receiveBufferCount];
            ////buffer=new byte[receiveBufferCount];
            //bufferManager = BufferManager.CreateBufferManager(receiveDataMaxLength * 10, _receiveBufferCount);
            _socket = socket;
            _isOwner = isOwner;
            _isNeedSpliteBag = isNeedSpliteBag;

            //dataPacketHandle = new DataPacketHandle(bufferManager);

            //_localPort = ((IPEndPoint)socket.LocalEndPoint).Port;
            //_remotePoint = (IPEndPoint)socket.RemoteEndPoint;

            //_internalReceiveAsync.AcceptSocket = socket;
            //_internalReceiveAsync.SetBuffer(_revBuffer, 0, _receiveBufferCount);
            //_internalReceiveAsync.SocketFlags = SocketFlags.None;
            //_internalReceiveAsync.Completed += OnInternalReceiveData;

            //_internalSendAsync.AcceptSocket = socket;
            ////_internalSendAsync.SetBuffer(new byte[_receiveBufferCount], 0, _receiveBufferCount);
            //_internalSendAsync.SocketFlags = SocketFlags.None;
            //_internalSendAsync.Completed += OnInternalReceiveData;

            //OnBeginReceive(0);

            //if (socket != null)
            //{
            //    //_revBuffer = bufferManager.TakeBuffer(_receiveBufferCount);
            //    socket.BeginReceive(_revBuffer, 0, _revBuffer.Length, SocketFlags.None, OnReceiveData, _revBuffer);
            //}

            Init();

        }

        public Socket Socket
        {
            get { return _socket; }
        }

        protected ArraySegment<byte> CodeSendData(byte[] sendStruct, int offset, int count)
        {
            if (!sendStruct.IsInEnableRange(offset, count))
            {
                return new ArraySegment<byte>(sendStruct, offset, count);
            }

            var newData = dataPacketHandle.PacketData(sendStruct, offset, count);

            return newData;
        }

        //protected void SendDataAsync(byte[] sendStruct, int offset, int count, EventHandler<DataAfterSendEventArgs> callback, object state)
        //{
        //    if (isClosed || !Socket.Connected)
        //    {
        //        return;
        //    }

        //    try
        //    {
        //        if (isClosed || !Socket.Connected)
        //        {
        //            return;
        //        }

        //        _socket.BeginSend(sendStruct, offset, count, SocketFlags.None, (iar) =>
        //                                                                           {
        //                                                                               Exception error = null;
        //                                                                               int sendCount = 0;
        //                                                                               try
        //                                                                               {
        //                                                                                   sendCount=_socket.EndSend(iar);
        //                                                                               }
        //                                                                               catch (Exception ex)
        //                                                                               {
        //                                                                                   error = ex;
        //                                                                               }

        //                                                                               var ev = new DataAfterSendEventArgs(this,new ArraySegment<byte>(sendStruct, offset, count),
        //                                                                                                                                         sendCount, error, state);

        //                                                                               EventHelper.Raise(callback, this, ev);

        //                                                                               OnAfterSendData(ev.SendedData,sendCount,error);
        //                                                                           }, null);
        //        // _socket.Send(sendStruct, offset, count, SocketFlags.None);
        //    }
        //    catch (Exception ex)
        //    {
        //        //Events.EventHelper.Raise(ExceptionHandle, this, new ExceptionHandleEventArgs(ex));
        //    }

        //    //}

        //    //this.Socket.Send(sendStruct, offset, count, SocketFlags.None);
        //}

        protected override void OnClose()
        {
            OnStop();

            if (_isOwner)
            {
                isClosed = true;

                try
                {
                    _socket.Shutdown(SocketShutdown.Both);
                }
                catch (Exception)
                {
                }

                try
                {
                    _socket.Close();
                }
                catch (Exception)
                {
                }

                //var t = Interlocked.Exchange(ref _revAsyncResult, null);

                //if (t!=null)
                //{
                //    t.Execute(new ArraySegment<byte>(null), new TransferClosedException());
                //}
            }

            //base.OnClose();

        }

        protected override bool OnStart()
        {
            if (!_isStop)
            {
                _isStop = false;
                OnBeginReceive(0);
                return true;
            }

            return false;
        }

        protected override bool OnStop()
        {
            if (!_isStop)
            {
                _isStop = true;

                if (_receiveAsync!=null)
                {
                    var temp = Interlocked.Exchange(ref _receiveAsync, null);

                    if (temp!=null)
                    {
                        temp.Error=new TransferStopedException();
                        temp.OnCompleted(this);
                    }
                }

                var t = _cacheReceiveData.Clear();

                if (t != null)
                {
                    foreach (var segment in t)
                    {
                        _bufferManager.ReturnBuffer(segment.Array);
                    }
                }

                return true;
            }

            return false;
        }

        #region "SendAsync"

        public override bool SendAsync(TransferAsyncEventArgs ea)
        {
            var retValue = true;

            if (isClosed || _isStop )//!Socket.Connected || )
            {
                ea.Error = new TransferClosedException();
                retValue = false;
            }

            if (_isStop)
            {
                ea.Error = new TransferStopedException();
                retValue = false;
            }

            OnBeforceSendData(ea);

            var v = CodeSendData(ea.Buffer, ea.Offset, ea.Count);

            //ea.SetBuffer(v.Array,v.Offset,v.Count);

            //_internalSendAsync.SetBuffer(v.Array,v.Offset,v.Count);
            //_internalSendAsync.UserToken = ea;
            //_internalSendAsync.Completed += OnSendCompleted;

            var socketEa = GetSendEventArgs(v.Array, v.Offset, v.Count);

            socketEa.UserToken = ea;

            if (retValue)
            {
                try
                {
                    //base.OnBeforceSendData(v.Array,v.Offset,v.Count,null);

                    retValue = _socket.SendAsync(socketEa);

                    if (!retValue)
                    {
                        if (socketEa.SocketError== SocketError.Success)
                        {
                            ea.BytesTransferred = socketEa.BytesTransferred;

                            ea.Error = null;

                            socketEa.Completed -= OnSendAsyncCompleted;

                            socketEa.Dispose();
                        }
                    }
                }
                catch (ObjectDisposedException)
                {
                    ea.Error = new TransferClosedException();
                    retValue = false;
                }
                catch (SocketException)
                {
                    ea.Error = new TransferDisconnectedException();
                    retValue = false;
                }


                if (!retValue && ea.Error==null)
                {
                    base.OnAfterSendData(ea);
                }
            }



            return retValue;
        }

        private void OnSendAsyncCompleted(object sender, SocketAsyncEventArgs e)
        {
            using (e)
            {
                e.Completed -= OnSendAsyncCompleted;

                var ea = (TransferAsyncEventArgs)e.UserToken;

                ea.BytesTransferred = e.BytesTransferred;

                ea.OnCompleted(this);

                base.OnAfterSendData(ea);
            }

            

        }

        private SocketAsyncEventArgsEx GetSendEventArgs(byte[] data, int offset, int count)
        {
            var temp = _socketAsyncPool.Take();

            temp.SetBuffer(data, offset, count);

            //ClearSocketAsyncEventArgs(temp);

            temp.Completed += OnSendAsyncCompleted;

            return temp;
        }

        #endregion

        #region "ReceiveAsync"

        public override bool ReceiveAsync(TransferAsyncEventArgs ea)
        {
            var retValue = true;

            if (isClosed || !Socket.Connected)
            {
                ea.Error = new TransferClosedException();
                retValue = false;
            }

            if (_isStop)
            {
                ea.Error = new TransferStopedException();
                retValue = false;
            }

            if (retValue)
            {
                if (Interlocked.CompareExchange(ref _receiveAsync, ea, null) == null)
                {
                    if (ReceiveComplated(null) != null)
                    {
                        retValue = false;
                    }                    
                }
                else
                {
                    //TODO:已有线程正在接收数据中,无法再接收
                    retValue = false;
                }
            }

            return retValue;
        }

        #endregion

        //private void ClearSocketAsyncEventArgs(SocketAsyncEventArgs e)
        //{
        //    e.Completed -= OnReceiveAsyncCompleted;
        //    e.Completed -= OnSendAsyncCompleted;
        //}

        //public override IAsyncResult BeginSend(byte[] data, int offset, int count, AsyncCallback callback, object state)
        //{
        //    if (isClosed || !Socket.Connected || _isStop)
        //    {
        //        return null;
        //    }

        //    if (!data.IsInEnableRange(offset, count))
        //    {
        //        throw new ArgumentOutOfRangeException("count");
        //    }

        //    var oldData = new ArraySegment<byte>(data, offset, count);

        //    //OnBeforceSendData(oldData, null);

        //    var v = CodeSendData(data, offset, count);

        //    IAsyncResult iar = null;
        //    try
        //    {
        //        iar = _socket.BeginSend(v.Array, v.Offset, v.Count, SocketFlags.None, callback, state);

        //        if (iar != null)
        //        {
        //            lock (_cacheAsyncResult)
        //            {
        //                _cacheAsyncResult.Add(iar, new TCPCallbackState(oldData, v));
        //            }
        //        }

        //    }
        //    catch (ObjectDisposedException ex)
        //    {
        //        OnTransferClosed();
        //        //Events.EventHelper.Raise(ExceptionHandle, this, new ExceptionHandleEventArgs(ex));
        //    }

        //    return iar;
        //}

        //public override int EndSend(IAsyncResult iar, ref Exception error)
        //{
        //    var sendCount = 0;

        //    try
        //    {
        //        sendCount = _socket.EndSend(iar);
        //    }
        //    catch (Exception ex)
        //    {
        //        error = ex;
        //    }

        //    TCPCallbackState callbackState;

        //    lock (_cacheAsyncResult)
        //    {
        //        if (_cacheAsyncResult.TryGetValue(iar, out callbackState))
        //        {
        //            _cacheAsyncResult.Remove(iar);
        //        }
        //    }

        //    //OnAfterSendData(callbackState.oldData, sendCount, error);

        //    if (dataPacketHandle.EnableSpliteBag)
        //    {
        //        _bufferManager.ReturnBuffer(callbackState.newData.Array);
        //    }


        //    return sendCount;
        //}

        //public IAsyncResult BeginReceive(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        //{
        //    if (callback == null)
        //    {
        //        throw new ArgumentNullException("callback");
        //    }

        //    if (count <= 0)
        //    {
        //        throw new ArgumentOutOfRangeException("count");
        //    }

        //    if (offset <= 0)
        //    {
        //        throw new ArgumentOutOfRangeException("offset");
        //    }

        //    if (buffer == null)
        //    {
        //        throw new ArgumentNullException("buffer");
        //    }

        //    if (buffer.IsInEnableRange(offset, count))
        //    {
        //        throw new ArgumentOutOfRangeException("buffer");
        //    }

        //    if (_isStop)
        //    {
        //        return null;
        //    }

        //    //var iar = new TCPReceiveAsyncResult(buffer, offset, count, callback, state);

        //    //if (Interlocked.CompareExchange(ref _revAsyncResult, iar, null) != null)
        //    //{
        //    //    throw new Exception("链接正在接收中，不允许再次调用BeginReceive");
        //    //}

        //    //_asyncResult = iar;

        //    //ThreadPool.QueueUserWorkItem(ReceiveComplated, iar);
        //    return null;

        //}

        //public int EndReceive(IAsyncResult iar, ref Exception error)
        //{
        //    //if (_revAsyncResult == null || _revAsyncResult != iar)
        //    //{
        //    //    error = new Exception("传入的Iar参数错误");
        //    //    return -1;
        //    //}

        //    //var count = _revAsyncResult.Count;

        //    //Interlocked.Exchange(ref _revAsyncResult, null);

        //    //var data = (ConnectReceiveAsyncResult) _asyncResult;

        //    //return count;

        //    return 0;
        //}

        public override bool IsStop
        {
            get { return _isStop; }
        }

        public override int ReceiveBufferCount
        {
            get { return _receiveBufferCount - 100; }
            set
            {
                if (value <= 0)
                {
                    throw new ArgumentOutOfRangeException(@"value");
                }

                Interlocked.Exchange(ref _receiveBufferCount, value);

                Interlocked.Add(ref _receiveBufferCount, 100);

                isBufferCountChanged = true;
            }
        }

        public override bool IsActivity
        {
            get { return _socket.Connected; }
        }

        public int LocalPort
        {
            get { return _localPort; }
        }

        public IPEndPoint RemotePoint
        {
            get { return _remotePoint; }
        }

        /// <summary>
        ///     设置用于分割不同分包的包头数据，默认为： 0x5A 0xA5
        /// </summary>
        public byte[] HeaderData
        {
            get
            {
                return dataPacketHandle.HeaderData;
            }
            set { dataPacketHandle.HeaderData = value; }
        }

        /// <summary>
        ///     是否启用连包处理
        /// </summary>
        public bool EnableSpliteBag
        {
            get
            {
                return dataPacketHandle.EnableSpliteBag;
            }
            set
            {
                dataPacketHandle.EnableSpliteBag = value;
            }
        }

        ///// <summary>
        /////     是否启用数据压缩（只压缩数据部分），如果启用，则接收方也应该同时启用该属性
        ///// </summary>
        //public bool EnableCompress
        //{
        //    get
        //    {
        //        return dataPacketHandle.EnableCompress;
        //    }
        //    set { dataPacketHandle.EnableCompress = value; }
        //}

        //public event EventHandler<DataSendEventArgs> BeforceSendData;
        //public event EventHandler<DataSendEventArgs> AfterSendData;
        //public event EventHandler<DataReceiveEventArgs> AfterReceiveData;




        private void OnInternalReceiveData(object sender, SocketAsyncEventArgs e)
        //private void OnReceiveData(IAsyncResult ar)
        {
            Exception error = null;
            int readCount = 0;
            //try
            //{
            //    readCount = _socket.EndReceive(ar);
            //}
            //catch (Exception ex)
            //{
            //    error = ex;
            //}

            if (e.SocketError != SocketError.Success)
            {
                error = new Exception(e.SocketError.ToString());
            }

            readCount = e.BytesTransferred;

            List<ArraySegment<byte>> retList = null;

            if (readCount > 0 && !_isStop)
            {
                retList = new List<ArraySegment<byte>>();

                int tempPos = 0;

                Interlocked.Exchange(ref tempPos, _lastRevBufferPos);

                //lock (lockerObj)
                //{
                var pos = dataPacketHandle.SplitPacket(_revBuffer, tempPos, readCount, retList);

                if (pos > tempPos)
                {
                    var moveCount = tempPos + readCount - pos;

                    if (moveCount > 0)
                    {
                        _revBuffer.MoveItems(pos, moveCount, 0);

                        Interlocked.Exchange(ref _lastRevBufferPos, moveCount);
                    }
                }
                // }
            }

            if (error == null && !isClosed && readCount != 0)
            {
                try
                {
                    if (isBufferCountChanged)
                    {
                        byte[] tempBuffer = null;

                        if (_receiveBufferCount != _revBuffer.Length)
                        {
                            tempBuffer = new byte[_receiveBufferCount];

                            if (_receiveBufferCount > _revBuffer.Length && _lastRevBufferPos > 0)
                            {
                                Array.Copy(_revBuffer, 0, tempBuffer, 0, _lastRevBufferPos);
                            }

                            _revBuffer = tempBuffer;
                        }

                        isBufferCountChanged = false;
                    }

                    OnBeginReceive(_lastRevBufferPos);

                    //_socket.BeginReceive(_revBuffer, _lastRevBufferPos, _revBuffer.Length - _lastRevBufferPos, SocketFlags.None,
                    //                OnReceiveData, null);
                }
                catch (Exception)
                {
                    Close();
                    //throw;
                }

            }

            if (retList != null && retList.Count > 0)
            {
                for (int i = 0; i < retList.Count; i++)
                {
                    //ReceiveComplated(retList[i], error);
                    //bufferManager.ReturnBuffer(retList[i].Array);

                    var temp = _cacheReceiveData.Enqueue(retList[i]);
                    if (temp.HasData())
                    {
                        _bufferManager.ReturnBuffer(temp.Array);
                    }
                }

                ReceiveComplated(null);
                //OnAfterReceiveData();
                //ThreadPool.QueueUserWorkItem(base., retList);
            }


            //if (error == null && !isClosed)
            //{
            //    var tempBuffer = bufferManager.TakeBuffer(_receiveBufferCount);
            //    _socket.BeginReceive(tempBuffer, 0, _receiveBufferCount, SocketFlags.None, OnReceiveData, tempBuffer);
            //}

            //var buff = (byte[])ar.AsyncState;

            //if (readCount > 0)
            //{
            //    OnAfterReceiveData(new ArraySegment<byte>(buff, 0, readCount), error);
            //}

            //bufferManager.ReturnBuffer(buff);

        }

        private void OnBeginReceive(int receiveBuffPos)
        {
            if (_socket != null && !_isStop && !_isDisposed && _socket.Connected)
            {
                try
                {
                    _internalReceiveAsync.SetBuffer(receiveBuffPos, _receiveBufferCount - receiveBuffPos);
                    _socket.ReceiveAsync(_internalReceiveAsync);
                    //_socket.BeginReceive(_revBuffer, receiveBuffPos, _revBuffer.Length - receiveBuffPos, SocketFlags.None, OnReceiveData, _revBuffer);
                }
                catch (Exception)
                {
                    Close();
                }
                //_revBuffer = bufferManager.TakeBuffer(_receiveBufferCount);

            }
        }

        private void OnTransferClosed()
        {

        }

        private void Init()
        {
            _receiveBufferCount += 100;
            _revBuffer = new byte[_receiveBufferCount];
            //buffer=new byte[receiveBufferCount];
            _bufferManager = BufferManager.CreateBufferManager(_receiveBufferCount * 10, _receiveBufferCount);
            //_socket = socket;
            //_isOwner = isOwner;

            dataPacketHandle = new DataPacketHandle(_bufferManager) { EnableSpliteBag = _isNeedSpliteBag };

            _localPort = ((IPEndPoint)_socket.LocalEndPoint).Port;
            _remotePoint = (IPEndPoint)_socket.RemoteEndPoint;

            //_internalReceiveAsync.AcceptSocket = socket;
            _internalReceiveAsync.SetBuffer(_revBuffer, 0, _receiveBufferCount);
            _internalReceiveAsync.SocketFlags = SocketFlags.None;
            _internalReceiveAsync.Completed += OnInternalReceiveData;

            _socketAsyncPool=new SocketAsyncEventArgsPool(100,1);

            //_internalSendAsync.AcceptSocket = socket;
            ////_internalSendAsync.SetBuffer(new byte[_receiveBufferCount], 0, _receiveBufferCount);
            //_internalSendAsync.SocketFlags = SocketFlags.None;
            //_internalSendAsync.Completed += OnInternalReceiveData;

            OnBeginReceive(0);
        }

        private class TCPCallbackState
        {
            public TCPCallbackState(ArraySegment<byte> oldData, ArraySegment<byte> newData)
            {
                this.oldData = oldData;
                this.newData = newData;
            }

            public ArraySegment<byte> oldData;
            public ArraySegment<byte> newData;
        }

        protected TransferAsyncEventArgs ReceiveComplated(object state)
        {
            //ArraySegment<byte> data;

            //if ((_receiveAsync != null) && _cacheReceiveData.TryDequeue(out data))
            //{
            //    if (_receiveAsync != null)
            //    {
            //        var copyCount = Math.Min(data.Count, _receiveAsync.Count);

            //        Buffer.BlockCopy(data.Array, data.Offset, _receiveAsync.Buffer, _receiveAsync.Offset, copyCount);

            //        _receiveAsync.BytesTransferred = copyCount;

            //        base.OnAfterReceiveData(_receiveAsync.Buffer, _receiveAsync.Offset, copyCount, _receiveAsync.Error);
            //    }

            //    _bufferManager.ReturnBuffer(data.Array);
            //}

            ArraySegment<byte> data;

            if (_receiveAsync != null && _cacheReceiveData.TryDequeue(out data))
            {
                var temp = _receiveAsync;

                temp = Interlocked.CompareExchange(ref _receiveAsync, null, temp);

                var copyCount = Math.Min(data.Count, temp.Count);

                Buffer.BlockCopy(data.Array, data.Offset, temp.Buffer, temp.Offset, copyCount);

                _bufferManager.ReturnBuffer(data.Array);

                temp.BytesTransferred = copyCount;

                base.OnAfterReceiveData(temp);

                return temp;
            }
            else
            {
                return null;
            }
        }

        //private class TCPReceiveAsyncResult:AsyncResultBase
        //{
        //    public TCPReceiveAsyncResult(byte[] data, int offset, int count, AsyncCallback callback, object state) : base(callback, state)
        //    {
        //        Offset = offset;
        //        Count = count;
        //        Data = data;
        //    }

        //    public byte[] Data { get; private set; }
        //    public int Offset { get; private set; }
        //    public int Count { get; private set; }

        //    public void Execute(ArraySegment<byte> data, Exception error)
        //    {

        //        int copyCount = 0;

        //        if (error == null)
        //        {
        //            if (data.Count > 0)
        //            {

        //                copyCount = Math.Min(data.Count, Count);

        //                Array.Copy(data.Array, data.Offset, Data, Offset, copyCount);
        //            }
        //        }

        //        Count = copyCount;

        //        if (error == null)
        //        {
        //            //var e = new DataReceiveEventArgs(, _conn.Item, DateTime.Now, error);

        //            OnAfterReceiveData(new ArraySegment<byte>(Data, 0, copyCount), error);
        //        }

        //        base.Execute(error);

        //    }
        //}

    }

    public class TCPTransferFactory : TransferFactoryBase, IDisposable
    {
        private ConcurrentDictionary<int, TcpSocketConfig> _cacheSocket = null;
        private object lockerObj = new object();
        private bool _isDisposed = false;
        //private Dictionary<IAsyncResult, TcpSocketConfig> _cacheAsyncResult = new Dictionary<IAsyncResult, TcpSocketConfig>();
        private SocketAsyncEventArgsPool _socketAsyncPool = new SocketAsyncEventArgsPool(100, 10);
        private LinkedListEx<ITransfer> _cacheTransfer = new LinkedListEx<ITransfer>();
        private TcpConnectStatePool _connectStatePool = new TcpConnectStatePool(100,10);

        public TCPTransferFactory()
        {
            _cacheSocket = new ConcurrentDictionary<int, TcpSocketConfig>();
        }

        #region "Listen"

        public override void Listen(TransferConnectConfig transferConfig)
        {
            if (transferConfig.Protocol == TransferProtocolType.TCP_SpliteBag)
            {
                var tcpConfig = (TCP_SpliteBag_ConnectConfig)transferConfig;
                AddPort(tcpConfig.LocalPort, true, tcpConfig.MaxLengthPerDataPack);
            }
            else if (transferConfig.Protocol == TransferProtocolType.TCP_Normal)
            {
                var tcpConfig = (TCP_Normal_ConnectConfig)transferConfig;
                AddPort(tcpConfig.LocalPort, false, tcpConfig.BufferLength);
            }
        }

        private void AddPort(int port, bool isEnableSpliteBag, int maxLengthPerDataPack)
        {
            if (_isDisposed)
            {
                return;
            }

            if (maxLengthPerDataPack < 1024)
            {
                throw new ArgumentOutOfRangeException("maxLengthPerDataPack");
            }

            if (_cacheSocket.ContainsKey(port))
            {
                return;
            }

            lock (lockerObj)
            {
                var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                socket.Bind(new IPEndPoint(IPAddress.Any, port));

                socket.Listen(1024);

                var t = new TcpSocketConfig();

                t.Port = port;
                t.Socket = socket;
                t.EnableSpliteBag = isEnableSpliteBag;
                t.InitMaxLengthPerDataPack = maxLengthPerDataPack;

                if(_cacheSocket.TryAdd(port, t))
                {
                    OnBeginAcceptConnect(socket);
                }
                else
                {
                    socket.Disconnect(false);
                    socket.Close();
                    socket.Dispose();
                }

                

            }


        }

        private void OnBeginAcceptConnect(Socket baseSocket)
        {
            var sea = _socketAsyncPool.Take();

            sea.Completed += OnAcceptCompleted;

            try
            {
                if (!baseSocket.AcceptAsync(sea))
                {
                    if (sea.SocketError == SocketError.Success)
                    {
                        OnAcceptCompleted(this, sea);
                    }

                    sea.Dispose();

                    if (!base._isClosed)
                    {
                        OnBeginAcceptConnect(baseSocket);
                    }
                }
            }
            catch (Exception)
            {

            }



            
        }

        private void OnAcceptCompleted(object sender, SocketAsyncEventArgs e)
        {
            e.Completed -= OnAcceptCompleted;

            var basePort = (int)e.UserToken;
            TcpSocketConfig config = null;


            if (e.SocketError == SocketError.Success)
            {
                var remotePoint = (IPEndPoint)e.AcceptSocket.LocalEndPoint;

                if (_cacheSocket.TryGetValue(remotePoint.Port, out config))
                {
                    var trans = new TCPTransferBase(e.AcceptSocket, config.EnableSpliteBag, config.InitMaxLengthPerDataPack, true);

                    trans.EnableSpliteBag = config.EnableSpliteBag;

                    var ee = new RemoteRequestConnectEventArgs(remotePoint, trans);

                    if (base.OnConnectRequest(ee))
                    {
                        trans.TransferClosed += trans_TransferClosed;

                        config.Transfers.AddLast(trans);

                        _cacheTransfer.AddLast(trans);

                        OnConnectSuccessed(new RemoteConenctSuccessEventArgs(trans));
                    }
                    else
                    {
                        try
                        {
                            e.AcceptSocket.Shutdown(SocketShutdown.Both);
                            e.AcceptSocket.Disconnect(false);

                            trans.Close();
                            trans.Dispose();
                        }
                        catch (Exception)
                        {
                        }
                        finally
                        {
                            try
                            {
                                e.AcceptSocket.Close();
                            }
                            catch (Exception)
                            {
                            }

                        }
                    }
                }
            }

            e.Dispose();

            if (config==null)
            {
                if (!_cacheSocket.TryGetValue(basePort,out config))
                {
                    return;
                }
            }

            if (config!=null)
            {
                OnBeginAcceptConnect(e.ConnectSocket);
            }

        }

        #endregion

        public override void StopListen(TransferConnectConfig port)
        {
            throw new NotImplementedException();
        }

        public override void ClosePort(TransferConnectConfig port)
        {
            var localPort = 0;

            if (port.Protocol == TransferProtocolType.TCP_SpliteBag)
            {
                var config = (TCP_SpliteBag_ConnectConfig)port;

                localPort = config.LocalPort;
            }
            else if (port.Protocol == TransferProtocolType.TCP_Normal)
            {
                var config = (TCP_Normal_ConnectConfig)port;

                localPort = config.LocalPort;

            }

            this.RemovePort(localPort);
        }

        //public override IAsyncResult BeginConnectTo(TransferConnectConfig transferConfig, AsyncCallback callback, object state)
        //{
        //    //maxRevDataLength += 100;

        //    var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        //    IPEndPoint remotePoint = null;
        //    bool enableSplite = false;
        //    int maxBagLength = 0;

        //    if (transferConfig.Protocol == TransferProtocolType.TCP_SpliteBag)
        //    {
        //        var config = (TCP_SpliteBag_ConnectConfig)transferConfig;

        //        remotePoint = config.RemotePoint;
        //        enableSplite = true;
        //        maxBagLength = config.MaxLengthPerDataPack;
        //    }
        //    else if (transferConfig.Protocol == TransferProtocolType.TCP_Normal)
        //    {
        //        var config = (TCP_Normal_ConnectConfig)transferConfig;

        //        remotePoint = config.RemotePoint;
        //        enableSplite = false;
        //        maxBagLength = config.BufferLength;
        //    }

        //    var iar = socket.BeginConnect(remotePoint, callback, state);

        //    //lock (lockerObj)
        //    //{
        //    //    _cacheAsyncResult.Add(iar, new TcpSocketConfig() { EnableSpliteBag = enableSplite, InitMaxLengthPerDataPack = maxBagLength, Port = 0, Socket = socket, IsClient = true });
        //    //}

        //    return iar;
        //}

        //public override ITransfer EndConnectTo(IAsyncResult iar, ref Exception error)
        //{
        //    TcpSocketConfig tcpConfig = null;

        //    //lock (lockerObj)
        //    //{
        //    //    if (_cacheAsyncResult.TryGetValue(iar, out tcpConfig))
        //    //    {
        //    //        _cacheAsyncResult.Remove(iar);
        //    //    }

        //    //}

        //    if (tcpConfig == null)
        //    {
        //        return null;
        //    }

        //    try
        //    {
        //        tcpConfig.Socket.EndConnect(iar);
        //    }
        //    catch (Exception ex)
        //    {
        //        error = ex;
        //    }

        //    if (error != null)
        //    {
        //        var trans = new TCPTransferBase(tcpConfig.Socket, tcpConfig.EnableSpliteBag, tcpConfig.InitMaxLengthPerDataPack, true);

        //        trans.EnableSpliteBag = tcpConfig.EnableSpliteBag;
        //        trans.TransferClosed += trans_TransferClosed;

        //        //tcpConfig.Transfers.Add(trans);

        //        //_cacheSocket.Add(((IPEndPoint)tcpConfig.Socket.LocalEndPoint).Port, tcpConfig);

        //        return trans;
        //    }
        //    else
        //    {
        //        return null;
        //    }



        //}

        #region "ConnectToAsync"

        public override bool ConnectToAsync(TransferAsyncEventArgs ea, TransferConnectConfig transferConfig)
        {
            if (ea==null)
            {
                throw new ArgumentNullException("ea");
            }

            if (transferConfig==null)
            {
                throw new ArgumentNullException("transferConfig");
            }

            var newsocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            var enableSplite = false;
            IPEndPoint remotePoint = null;
            int maxBagLength = 0;

            if (transferConfig.Protocol == TransferProtocolType.TCP_SpliteBag)
            {
                var config = (TCP_SpliteBag_ConnectConfig)transferConfig;

                remotePoint = config.RemotePoint;
                enableSplite = true;
                maxBagLength = config.MaxLengthPerDataPack;
            }
            else if (transferConfig.Protocol == TransferProtocolType.TCP_Normal)
            {
                var config = (TCP_Normal_ConnectConfig)transferConfig;

                remotePoint = config.RemotePoint;
                enableSplite = false;
                maxBagLength = config.BufferLength;
            }

            var sea = _socketAsyncPool.Take();

            sea.RemoteEndPoint = remotePoint;

            var ut=_connectStatePool.Take();
            ut.EnableSpliteBag = enableSplite;
            ut.InitMaxLengthPerDataPack = maxBagLength;
            ut.EventArgs = ea;

            sea.UserToken = ut;

            sea.Completed += OnConnectCompleted;

            

            try
            {
                if(!newsocket.ConnectAsync(sea))
                {
                    if (sea.SocketError== SocketError.Success)
                    {
                        OnConnectCompleted(this,sea);
                        return false;
                    }
                    else
                    {
                        ea.Error = new TransferConnectException(sea.SocketError.ToStringEx());
                        return false;
                    }
                }
                else
                {
                    return true;
                }
            }
            catch (Exception ex)
            {
                ea.Error=new TransferException(ex);
                return false;
            }
        }

        private void OnConnectCompleted(object sender,SocketAsyncEventArgs e)
        {
            e.Completed -= OnConnectCompleted;

            using (var state = (TcpConnectState)e.UserToken)
            {
                var trans = new TCPTransferBase(e.ConnectSocket, state.EnableSpliteBag, state.InitMaxLengthPerDataPack, true);

                e.UserToken = null;

                trans.EnableSpliteBag = state.EnableSpliteBag;
                trans.TransferClosed += trans_TransferClosed;

                _cacheTransfer.AddLast(trans);

                state.EventArgs.Transfer = trans;

                state.EventArgs.OnCompleted(trans);                
            }

        }

        #endregion

        public void RemovePort(int port)
        {
            if (_isDisposed)
            {
                return;
            }

            TcpSocketConfig config = null;

            if (_cacheSocket.TryRemove(port,out config))
            {
                if (config.Transfers.Count>0)
                {
                    foreach (var transfer in config.Transfers)
                    {
                        transfer.Close();
                    }

                    config.Transfers.Clear();                    
                }

                try
                {
                    config.Socket.Shutdown(SocketShutdown.Both);
                    config.Socket.Disconnect(false);
                }
                catch (Exception)
                {
                }
                finally
                {
                    try
                    {
                        config.Socket.Close();
                    }
                    catch (Exception) { }

                }

            }



            //var config = _cacheSocket.TryGetValue(port, null);

            //if (config == null)
            //{
            //    return;
            //}

            //lock (lockerObj)
            //{


            //}




            //_cacheSocket.Remove(port);

        }

        public ITransfer ConnectTo(int localPort, IPEndPoint remotePoint, bool isEnableSpliteBag, int maxLengthPerDataPack)
        {
            var config = _cacheSocket.TryGetValue(localPort);

            Socket socket = null;


            if (config == null)
            {
                lock (lockerObj)
                {
                    config = _cacheSocket.TryGetValue(localPort);

                    if (config == null)
                    {
                        config = new TcpSocketConfig();

                        config.Port = localPort;
                        config.InitMaxLengthPerDataPack = maxLengthPerDataPack;

                        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                        socket.Bind(new IPEndPoint(IPAddress.Any, localPort));
                        //socket.Listen(1024);

                        config.Socket = socket;

                        //_cacheSocket.Add(localPort, config);
                    }

                }
            }

            if (socket == null)
            {
                return null;
            }

            Exception error = null;
            var au = new AutoResetEvent(false);


            socket.BeginConnect(remotePoint, (iar) =>
                                             {
                                                 try
                                                 {
                                                     socket.EndConnect(iar);
                                                 }
                                                 catch (Exception ex)
                                                 {
                                                     error = ex;
                                                 }

                                                 au.Set();
                                             }, null);

            //socket.Connect(remotePoint);    
            if (!au.WaitOne(20000))
            {
                try
                {
                    socket.Disconnect(false);
                }
                catch (Exception)
                {
                    throw new TimeoutException("远程计算机连接超时");
                }
                finally
                {
                    socket.Close();
                }
            }

            if (error != null)
            {
                throw error;
            }


            var trans = new TCPTransferBase(socket, isEnableSpliteBag, maxLengthPerDataPack, true);

            trans.EnableSpliteBag = isEnableSpliteBag;

            //config.Transfers.Add(trans);

            return trans;
        }

        public override void Close()
        {
            lock (lockerObj)
            {
                _isDisposed = true;

                foreach (var config in _cacheSocket)
                {
                    var transferList = config.Value.Transfers;

                    foreach (var transfer in transferList)
                    {
                        if (transfer != null)
                        {
                            transfer.TransferClosed -= trans_TransferClosed;
                            transfer.Dispose();
                        }
                    }

                    transferList.Clear();
                }

                _cacheSocket.Clear();

                _cacheSocket = null;
            }
        }

        #region Implementation of IDisposable

        public void Dispose()
        {
            if (!_isDisposed)
            {
                lock (lockerObj)
                {
                    Close();

                    GC.SuppressFinalize(this);
                }
            }

        }

        #endregion



        //private void OnAcceptedConnect(IAsyncResult iar)
        //{
        //    var s = (TcpSocketConfig)iar.AsyncState;

        //    Socket acceptSocket = null;

        //    try
        //    {
        //        acceptSocket = s.Socket.EndAccept(iar);
        //    }
        //    catch (Exception)
        //    {

        //    }

        //    if (_isDisposed)
        //    {
        //        return;
        //    }

        //    if (acceptSocket != null)
        //    {
        //        var trans = new TCPTransferBase(acceptSocket, s.InitMaxLengthPerDataPack, true);

        //        trans.EnableSpliteBag = s.EnableSpliteBag;

        //        var remotePoint = (IPEndPoint)acceptSocket.RemoteEndPoint;

        //        var e = new RemoteRequestConnectEventArgs(remotePoint, trans);


        //        if (base.OnConnectRequest(e))
        //        {
        //            trans.TransferClosed += trans_TransferClosed;

        //            var config = _cacheSocket.TryGetValue(trans.LocalPort);

        //            //config.Transfers.Add(trans);

        //            OnConnectSuccessed(new RemoteConenctSuccessEventArgs(trans));

        //            //if (ConnectSuccessed != null)
        //            //{
        //            //    ConnectSuccessed(this, );
        //            //}

        //        }
        //        else
        //        {
        //            try
        //            {
        //                acceptSocket.Shutdown(SocketShutdown.Both);
        //                acceptSocket.Disconnect(false);
        //            }
        //            catch (Exception)
        //            {
        //            }
        //            finally
        //            {
        //                try
        //                {
        //                    acceptSocket.Close();
        //                }
        //                catch (Exception) { }

        //            }
        //        }

        //    }

        //    s.Socket.BeginAccept(OnAcceptedConnect, null);
        //}

        void trans_TransferClosed(object sender, TransferClosedEventArgs e)
        {
            e.Transfer.TransferClosed -= trans_TransferClosed;

            var trans = (INetTransfer)e.Transfer;

            TcpSocketConfig config;

            if (_cacheSocket.TryRemove(trans.LocalPort,out config))
            {
                config.Transfers.Remove(e.Transfer);
            }

            _cacheTransfer.Remove(e.Transfer);

            //var config = _cacheSocket.TryGetValue(trans.LocalPort);

            //config.Transfers.Remove(e.Transfer);

            ////if (config.IsClient)
            //{
            //    lock (lockerObj)
            //    {
            //        _cacheSocket.TryRemove(config.Port);
            //    }
            //}
        }

        private class TcpSocketConfig
        {
            public TcpSocketConfig()
            {
                Transfers = new LinkedList<ITransfer>();
                EnableSpliteBag = true;
            }

            public int Port;
            public Socket Socket;
            public bool EnableSpliteBag;
            public int InitMaxLengthPerDataPack;
            public LinkedList<ITransfer> Transfers { get; private set; }
        }

        private class TcpConnectState : IRecyclable
        {
            private IRecyclablePool<TcpConnectState> _pool;

            public TcpConnectState(IRecyclablePool<TcpConnectState> pool)
            {
                _pool = pool;
            }

            public int Port;
            public bool EnableSpliteBag;
            public int InitMaxLengthPerDataPack;
            public TransferAsyncEventArgs EventArgs;

            #region Implementation of IDisposable

            public void Dispose()
            {
                Port = 0;
                EnableSpliteBag = false;
                InitMaxLengthPerDataPack = 0;
                EventArgs = null;

                GC.SuppressFinalize(this);
            }

            #endregion

            #region Implementation of IRecyclable

            public IRecyclablePool<IRecyclable> Pool
            {
                get { return (IRecyclablePool<IRecyclable>)_pool; }
                set { _pool = (IRecyclablePool<TcpConnectState>)value; }
            }

            /// <summary>
            ///     当对象真正释放的时候，回收器会调用该函数
            /// </summary>
            public void DisposeObject()
            {
                Port = 0;
                EnableSpliteBag = false;
                InitMaxLengthPerDataPack = 0;
                EventArgs = null;
                _pool.RecycleObject(this);
            }

            #endregion
        }

        private class TcpConnectStatePool : RecyclablePool<TcpConnectState>
        {
            public TcpConnectStatePool(int maxLength,int minLength):base(maxLength,minLength)
            {
                base.Init();
            }

            #region Overrides of RecyclablePool<TcpConnectState>

            /// <summary>
            ///     构建一个新的对象,该函数必须由继承的类实现
            /// </summary>
            /// <returns></returns>
            protected override TcpConnectState CreateRecyclableObject()
            {
                return new TcpConnectState(this);
            }

            #endregion
        }
    }

    //public class TCPTransferClientFactory
    //{
    //    public ITransfer ConnectTo(IPEndPoint endPoint, int maxRevDataLength)
    //    {
    //        if (maxRevDataLength <= 1024)
    //        {
    //            throw new ArgumentOutOfRangeException("maxRevDataLength", "单数据包预设最大长度必须大于1024");
    //        }

    //        maxRevDataLength += 100;

    //        var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    //        try
    //        {
    //            socket.Connect(endPoint);
    //        }
    //        catch (Exception)
    //        {
    //            return null;
    //        }

    //        if (socket.Connected)
    //        {
    //            return new TCPTransferBase(socket, maxRevDataLength);
    //        }
    //        else
    //        {
    //            return null;
    //        }


    //    }
    //}

    /// <summary>
    ///     如果是Listen的用途,则使用LocalPort属性一个
    /// </summary>
    public class TCP_Normal_ConnectConfig : TransferConnectConfig
    {
        public TCP_Normal_ConnectConfig(int localPort, IPEndPoint remotePoint)
        {
            LocalPort = localPort;
            RemotePoint = remotePoint;
        }

        public int LocalPort { get; private set; }

        public IPEndPoint RemotePoint { get; private set; }

        public override TransferProtocolType Protocol
        {
            get { return TransferProtocolType.TCP_Normal; }
        }
    }

    public class TCP_SpliteBag_ConnectConfig : TransferConnectConfig
    {
        public TCP_SpliteBag_ConnectConfig(int localPort, IPEndPoint remotePoint, int maxLengthPerDataPack)
        {
            LocalPort = localPort;
            RemotePoint = remotePoint;
            MaxLengthPerDataPack = maxLengthPerDataPack;
        }

        public int LocalPort { get; private set; }

        public IPEndPoint RemotePoint { get; private set; }

        public override TransferProtocolType Protocol
        {
            get { return TransferProtocolType.TCP_Normal; }
        }

        public int MaxLengthPerDataPack { set; get; }
    }


    public class RemoteRequestConnectEventArgs : EventArgs
    {
        public RemoteRequestConnectEventArgs(IPEndPoint s, ITransfer transfer)
        {
            RemotePoint = s;
            IsEnableConnect = true;
            Transfer = transfer;
        }

        public bool IsEnableConnect { set; get; }

        public IPEndPoint RemotePoint { private set; get; }

        public ITransfer Transfer { get; private set; }
    }

    public class RemoteConenctSuccessEventArgs : EventArgs
    {
        public RemoteConenctSuccessEventArgs(ITransfer transfer)
        {
            Transfer = transfer;
        }

        public ITransfer Transfer { get; private set; }
    }

    /// <summary>
    ///     <para>
    ///         处理数据的封包以及分包
    ///     </para>
    ///     
    ///         可能会出现的问题:<br/>
    ///         因为该类本身只负责分包以及封包的功能,不存在对数据包的缓存,所以,如果接收的数据包过大,很可能会出现无法正常分包,
    ///         比如:要接收的数据包的大小为10K,当前的接收缓冲区之后8K,根据算法,分包函数会返回0,表示需要等待数据包接收完毕,
    ///         此时则需要加大buff的缓存区大小
    ///             
    /// </summary>
    /// <remarks>
    ///     数据包结构:<br/>`
    ///             数据包起始标识(5AA5) + 数据区长度(int) + 实际数据区 + CRC校验码<br/>
    /// 
    ///     分包实现算法:<br/>
    ///         1.找到第一个与HeaderData[0]相同的数据,如果该数据包包头不完整,并且已经达到buff的末尾时,返回当前的指针<br/>
    ///         如果未达到末尾,则往后继续寻找完整的数据包头信息,并读取数据区长度<br/>
    ///         2.如果寻找到完整的数据包包头信息,则检查剩余的缓冲区内的数据是否足够的数据,如果数据不足,则返回当前的指针位置
    ///         如果数据长度足够,则从缓冲区中申请一块指定大小的缓存,然后新建一个ArraySegment类,存放指定的缓存区,并复制数据
    ///         到该区域,最后重新计算实际数据区的偏移和大小
    ///         3.如果未启用分包的话,则直接将数据包打包为ArraySegment类返回
    ///         
    /// </remarks>
    internal class DataPacketHandle
    {
        private const int CRCLength = 2;    //CRC校验码长度,,4为32位CRC校验,2为16位CRC
        private const int DataCountFieldLength = 4;//存放数据去长度的变量长度,,,32位的数据

        private readonly static byte[] defaultHeaderData = new byte[] { 0x5A, 0xA5 };  //默认的数据包包头为： 5A A5
        private byte[] _headerData = null;
        private int _headerDataLength = 0;

        public DataPacketHandle() : this(null) { }

        public DataPacketHandle(BufferManager bufferManager)
        {

            BufferManage = bufferManager;

            _headerData = new byte[defaultHeaderData.Length];

            defaultHeaderData.CopyTo(_headerData, 0);

            EnableSpliteBag = true;
            EnableCompress = false;
        }

        public BufferManager BufferManage { set; get; } //缓冲区

        /// <summary>
        ///     设置用于分割不同分包的包头数据，默认为： 0x5A 0xA5
        /// </summary>
        public byte[] HeaderData
        {
            get
            {
                return _headerData;
            }
            set
            {
                if (value == null || value.Length <= 0 || value == _headerData)
                {
                    throw new ArgumentOutOfRangeException("value");
                }

                bool isSame = true;

                if (value.Length != _headerData.Length)
                {
                    isSame = false;
                }
                else
                {
                    for (int i = 0; i < value.Length; i++)
                    {
                        if (value[i] != _headerData[i])
                        {
                            isSame = false;
                        }
                    }
                }

                if (!isSame)
                {
                    _headerData = new byte[value.Length];
                    value.CopyTo(_headerData, 0);
                    _headerDataLength = value.Length;
                    //_headerData = value;
                }

            }
        }

        /// <summary>
        ///     是否启用连包处理
        /// </summary>
        public bool EnableSpliteBag { set; get; }

        /// <summary>
        ///     是否启用数据压缩（只压缩数据部分），如果启用，则接收方也应该同时启用该属性
        /// </summary>
        public bool EnableCompress { set; get; }

        /// <summary>
        ///     将数据进行分包处理,分包后的结果存放在dataList参数中
        ///     注： 如果输入缓冲区中，包含未接收完成的数据包段，则函数返回最后一个未接收完成的数据包的包头起始索引；
        ///             如果输入缓冲区的数据为无效数据，则函数返回offset；
        ///             如果输入缓冲区的数据全部都为完整的数据包，则返回offset。
        /// </summary>
        /// <param name="buff">输入缓冲区</param>
        /// <param name="offset">缓冲区偏移量</param>
        /// <param name="count">缓冲区中有效数据长度</param>
        /// <param name="dataList">用于存放分包后的数据包列表</param>
        /// <returns></returns>
        public int SplitPacket(byte[] buff, int offset, int count, List<ArraySegment<byte>> dataList)
        {
            if (!EnableCompress && !EnableSpliteBag)    //如果不需要处理分包和压缩的话,直接返回
            {
                dataList.Add(new ArraySegment<byte>(buff, offset, count));
                return 0;
            }

            ArraySegmentEx<byte> currentDataBlock = null;
            var currentPos = offset;

            //预先计算好常用的值，省了每次都要去计算

            var srcBufferMaxPos = offset + count;   //原缓存区中，最大的偏移值
            var packetHeaderLength = HeaderData.Length + DataCountFieldLength;  //包头的总长度
            var packetTotalInfoLength = packetHeaderLength + CRCLength; //数据包中，总共用于描述的信息长度，包头+CRC校验


            while (currentPos < srcBufferMaxPos)
            {
                var newPos = GetNextPacketStartIndex(buff, currentPos, srcBufferMaxPos); //查找下一个包含包头数据的起始索引

                if (newPos == -1)   //如果剩下的数据中不存在指定的数据包包头信息则退出循环处理
                {
                    currentPos = 0;
                    break;
                }

                if (newPos + packetHeaderLength > srcBufferMaxPos)  //如果数据包头部不完整,则留待下次继续处理
                {
                    break;
                }

                var dataCount = BitConverter.ToInt32(buff, newPos + HeaderData.Length);  //读取数据区长度标识

                if (dataCount <= 0)
                {
                    //当读取出来的数据长度小于0时，当前指针加1，继续循环
                    currentPos++;
                    continue;
                }

                byte[] block = null;

                //缓冲区的大小： 数据区长度+4个字节的int（数据区长度标识）+包头标识长度+CRC校验码长度
                int requestBlockSize = dataCount + packetTotalInfoLength;

                if (BufferManage != null)
                {
                    //向缓存管理类申请指定空间大小的缓冲区，
                    block = BufferManage.TakeBuffer(requestBlockSize);
                }
                else
                {
                    block = new byte[requestBlockSize];
                }

                //新建一个对象
                currentDataBlock = new ArraySegmentEx<byte>(block, 0, dataCount + packetTotalInfoLength);

                //将当前指针移动到新的包头数据前
                currentPos = newPos;

                //如果当前缓冲区中剩余的数据量不足,则不进行处理,留待下次,并返回当前的指针位置
                if (buff.Length - currentPos - currentDataBlock.Count < 0)
                {
                    break;
                }

                //计算可以复制的数据数量，（当前剩余需要复制的数量与当前输入数据区中剩余数量，两个值中取小值）
                var copyCount = Math.Min(currentDataBlock.Count - currentDataBlock.Offset, count);

                //数组复制
                Array.Copy(buff, currentPos, currentDataBlock.Array, currentDataBlock.Offset, copyCount);

                currentDataBlock.Offset += packetHeaderLength;  //计算实际数据区的起始位置，跳过数据包包头与数据长度区
                currentDataBlock.Count -= packetTotalInfoLength; //重新计算数据长度，减去包头与CRC校验的长度

                //对数据进行CRC校验
                if (currentDataBlock.Array.GetCRCCode(currentDataBlock.Offset, currentDataBlock.Count + CRCLength) == 0) //校验CRC
                {
                    var totalCount = currentDataBlock.Count;

                    if (EnableCompress) //如果启用了压缩,,则解压缩数据区
                    {
                        totalCount = currentDataBlock.Array.UnCompressToByteArray(currentDataBlock.Offset,
                                                                                  totalCount,
                                                                                  currentDataBlock.Array,
                                                                                  currentDataBlock.Offset);
                    }

                    currentDataBlock.Count = totalCount;

                    dataList.Add(currentDataBlock); //放入结果集中

                    currentPos += copyCount;    //移动总复制进度的指针到下个位置
                }
                else //如果校验错误，则回收之前申请的数据缓冲区
                {
                    if (BufferManage != null)
                    {
                        currentPos++;
                        BufferManage.ReturnBuffer(currentDataBlock.Array);
                    }
                }



                //}
            }

            //if (moveItemIndex > 0 && moveCount > 0)
            //{
            //    buff.MoveItems(moveItemIndex, moveCount, 0);
            //}

            return currentPos;
        }

        /// <summary>
        ///     封包数据,如添加分包包头信息,压缩数据等
        /// </summary>
        /// <param name="buff">输入缓冲区</param>
        /// <param name="offset">缓冲区偏移量</param>
        /// <param name="count">缓冲区中有效数据长度</param>
        /// <returns>返回封包后的数据</returns>
        public ArraySegment<byte> PacketData(byte[] buff, int offset, int count)
        {
            //using (var ms = new MemoryStream(count + HeaderData.Length + DataCountFieldLength + CRCLength))
            //{
            //if (EnableCompress)
            //{
            //    buff = buff.CompressToByteArray();
            //    count = buff.Length;
            //    offset = 0;
            //}

            ArraySegment<byte> result;

            if (EnableSpliteBag)
            {
                var writecount = HeaderData.Length + 4 + count + 2;

                var newbuff = BufferManage.TakeBuffer(writecount);

                Array.Copy(_headerData, 0, newbuff, 0, _headerDataLength);
                //ms.Write(HeaderData, 0, HeaderData.Length);

                Array.Copy(BitConverter.GetBytes(count), 0, newbuff, _headerDataLength, 4);
                //ms.Write(BitConverter.GetBytes(count), 0, 4);

                Array.Copy(buff, 0, newbuff, _headerDataLength + 4, buff.Length);
                //ms.Write(buff, offset, count);

                Array.Copy(BitConverter.GetBytes(buff.GetCRCCode(offset, count)), 0, newbuff, _headerDataLength + 4 + buff.Length, 2);
                //ms.Write(BitConverter.GetBytes(buff.GetCRCCode(offset, count)), 0, 2);

                result = new ArraySegment<byte>(newbuff, 0, writecount);
            }
            else
            {
                result = new ArraySegment<byte>(buff, offset, count);

                //ms.Write(buff, offset, count);
            }

            //ms.Seek(0, SeekOrigin.Begin);

            //var newData = ms.ReadAllBytes();

            return result;
            //}
        }

        private int GetNextPacketStartIndex(byte[] buff, int offset, int endOffset)
        {
            var currentPos = offset;
            var newPos = -1;
            var headerFirst = HeaderData[0];
            //var lastOffset = offset + count;

            while (currentPos < endOffset)
            {
                newPos = Array.FindIndex(buff, currentPos, (s) => s == headerFirst);

                if (newPos < 0)
                {
                    break;
                }

                var checkLength = Math.Min(HeaderData.Length, buff.Length - 1 - newPos);

                var isSame = true;

                for (int i = 0; i < checkLength; i++)
                {
                    if (buff[newPos + i] != HeaderData[i])
                    {
                        isSame = false;
                        continue;
                    }
                }

                if (isSame)
                {
                    break;
                }
                else
                {
                    currentPos = newPos + 1;
                }
            }

            return newPos;

        }
    }


    public class ExceptionHandleEventArgs : EventArgs
    {
        public ExceptionHandleEventArgs(Exception error)
        {
            Error = error;
            IsHandled = false;
        }

        public Exception Error { protected set; get; }
        public bool IsHandled;
    }



}
